#ifndef EXP_INDICES_HPP
#define EXP_INDICES_HPP

#include<type_traits>
#include<array>

#include "exp_list.hpp"

namespace exp_utilities
{
	struct below_zero {
		static constexpr size_t value = 0;
	};

	template<std::size_t I>
    using Idx = std::integral_constant<std::size_t, I>;

	namespace exp_indices_details {
		template<class _Idx, size_t _I>
		struct Add_Idx {};

		template<std::size_t I> struct Add_Idx<below_zero, I>
		{
			using type = Idx<0>;
		};
		template<template<size_t> class idx, std::size_t I_in_Idx, std::size_t I>
		struct Add_Idx<idx<I_in_Idx>, I>
		{
			using type = idx<I_in_Idx + I>;
		};
		template<std::size_t I, size_t ADD>
		struct Add_Idx<Idx<I>, ADD> {
			using type = Idx<I + ADD>;
		};
	}
	

	template<class idx, size_t I>
	using add_idx_t = typename exp_indices_details::Add_Idx<idx, I>::type;

	template<class _Idx>
	using inc_idx_t = add_idx_t<_Idx, 1>;


	template<size_t ..._elements>
	struct meta_array : exp_list<Idx<_elements>...>
	{
		using cv_typelist = exp_list<Idx<_elements>...>;
		template<template<size_t...I> class integer_array_type>
		using to = integer_array_type<_elements...>;
		template<size_t I> using at = exp_select<I, cv_typelist>;
		template<size_t I> static constexpr size_t get() {
			return at<I>::value;
		}

		static constexpr size_t length = cv_typelist::length;
		static constexpr size_t sum = (0 + ... + _elements);
		static consteval std::array<std::size_t, length> array() {
			return { _elements... };
		}
	};

	template<class TL>
	struct to_meta_array {
		static_assert(false, "not all elements has value");
	};

	template<template<class...> class integer_wrapper, std::size_t ...elements>
	struct to_meta_array<integer_wrapper<Idx<elements>...>> {
		using type = meta_array<elements...>;
	};

	template<class TL>
	using to_meta_array_t = get_type<to_meta_array<TL>>;
}
#endif // !EXP_INDICES_HPP
